<?php


class SBlamMailExploit extends SblamTestPost
{

    function testPost(ISblamPost $p)
    {
        $score = $this->checkFragment($p->getRawContent());
        
        if ($a = $p->getAuthorName())
        {
            $score += $this->checkFragment($a); if (false !== strpos($a,"\n")) $score += 5;
        }
        if ($a = $p->getAuthorEmail())
        {
            $score += $this->checkFragment($a); if (false !== strpos($a,"\n")) $score += 5;
        }
        if ($a = $p->getAuthorURI())
        {
            $score += $this->checkFragment($a); if (false !== strpos($a,"\n")) $score += 5;
        }
        
        if ($score > 28) return array(4,self::CERTAINITY_SURE,"Cerainly a mail exploit");
        if ($score > 23) return array(2,self::CERTAINITY_SURE,"Mail exploit");
        if ($score > 17) return array(1,self::CERTAINITY_HIGH,"Mail exploit");
        if ($score > 11) return array(0.6,self::CERTAINITY_NORMAL,"Possible mail exploit");
        return NULL;
    }


    function checkFragment($txt)
    {
        if (!$txt) return 0;
        
        if (false===strpos($txt,"\n")) $txt = urldecode($txt);
        if (false===strpos($txt,"\n")) $txt = html_entity_decode($txt,ENT_QUOTES,'UTF-8');
        
        $parts = preg_split('!\r?\n\r?\n!',$txt,2); // split headers/body
        $parts[0] = preg_replace('!\r?\n[ \t]+!',' ',$parts[0]); // fold multiline headers

        $this->hasboundary=false;
        $score = 2 * $this->lookForHeaders($parts[0]);  // headers should be in head. otherwise they're just quoted
        if (isset($parts[1])) 
        {
            if ($this->hasboundary && preg_match('!^--+[a-z0-9]{5,}--+\s*$!im',$parts[1])) {d('found boundary end');$score += 3;} 
            $score += $this->lookForHeaders($parts[1]);
            
            if (preg_match('!\n.(?:\r?\n|$)!',$parts[1])) {d('enddot');$score+=2;}
            if (preg_match('!^(?:[/ \t\r\n+-]*[a-zA-Z0-9]){10,}[=\s.]*$!',$parts[1])) {d('base64f');$score += 2;}
        }
        //d($parts,"scored $score points");
        return $score;
    }

    protected $hasboundary;
    function lookForHeaders($txt)
    {
        $score = 0;
             if (preg_match('!^Content-Transfer-Encoding\s*:!im',$txt))  $score+=4;
             if (preg_match('!^Content-Type\s*:\s*multipart!im',$txt)) $score+=4;
        else if (preg_match('!^Content-Type\s*:\s*text/plain!im',$txt)) $score+=3;
        else if (preg_match('!^Content-Type\s*:\s*!im',$txt)) $score+=2;
        
        if (preg_match('!^Subject\s*:!im',$txt)) $score += 1;
        if (preg_match('!^X-Mailer\s*:!im',$txt)) $score += 2;
        if ($cnt = preg_match_all('!^(?:To|BCC|CC)\s*:!im',$txt,$whatever)) $score += 0.5 + min($cnt/2,5);
        if (($cnt = preg_match_all('![a-z0-9.+-]+@[a-z0-9-]+\.[a-z0-9-]+!i',$txt,$whatever)) && $cnt>2) $score += 1 + min(4,$cnt/3);
        if (preg_match('!;\s*boundary\s*=\s*[\"\']?[a-z0-9-]{5,}!i',$txt)) {d('found boundary start');$this->hasboundary = true; $score += 2;}
        return $score;
    }


    static function info()
    {
        return array(
            'name'=>'Mail header injection exploit',
            'desc'=>'Looks for attempts to exploit primitve mail() functions, like the one in PHP',
            'remote'=>false,
        );
    }
}

